﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LingDong.HtmlParser;
using PanGu;
using LingDong.IndexReader;
using LingDong.IndexEntities.Document;

namespace LingDong.KeywordExtract
{
    public class KeywordExtracter
    {
        public static List<string> GetHTMLKeywords(int did, Index index)
        {
            OriginalDocumentEnity doc = index.GetOriginalDocument(did);
            return GetHTMLKeywords(doc.Content, index);
        }

        public static List<string> GetHTMLKeywords(string html, Index index)
        {
            if (!isPanGuInited)
            {
                Segment.Init();
                isPanGuInited = true;
            }

            string content = HtmlContent.GetHtmlContent(html);
            Dictionary<string, double> wordRank = new Dictionary<string, double>();

            List<WordInfo> wordList = new Segment().DoSegment(content).ToList();
            int wordNum = wordList.Count;
            for (int i = 0; i < wordNum; i++)
            {
                if (wordRank.ContainsKey(wordList[i].Word))
                    continue;

                wordRank[wordList[i].Word] = RankWord(wordList[i], index, i, wordNum);
            }

            List<string> keywordList = (from word in wordRank
                                        orderby word.Value descending
                                        select word.Key).
                                        Take(Settings.KeyWordNumberPerDoc).ToList();

            return keywordList;
        }

        public static string GetHTMLKeywordsString(string html, Index index)
        {
            List<string> keywords = GetHTMLKeywords(html, index);
            StringBuilder result = new StringBuilder();
            foreach (string keyword in keywords)
            {
                result.Append(keyword);
                result.Append("\t");
            }
            return result.ToString();
        }


        #region Rank

        private static double RankWord(WordInfo wordInfo, Index index, int wordPosition, int totalWordNum)
        {
            double positionScore = GetPositionScore(wordPosition, totalWordNum);
            double freqScore = GetFrequenceScore(wordInfo.Word, index);
            double linguisticScore = GetLinguisticScore(wordInfo);
            return positionScore * freqScore * linguisticScore;
        }

        /// <summary>
        /// position score, by word order
        /// </summary>
        /// <param name="wordPosition"></param>
        /// <param name="totalWordNum"></param>
        /// <returns></returns>
        private static double GetPositionScore(int wordPosition, int totalWordNum)
        {
            double positionScore = (double)wordPosition / totalWordNum;
            positionScore = 1.0 / (0.1 + positionScore);
            positionScore = 10 * Math.Log10(1.0 + positionScore);
            return positionScore;
        }

        /// <summary>
        /// frequence score, tf-idf
        /// </summary>
        /// <param name="word"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private static double GetFrequenceScore(string word, Index index)
        {
            int df = index.GetTermDocumentFrequency(word);
            if (df == 0)
                return 0.0;

            double idf = (double)index.GetTotalDocumentCount() / df;
            return Math.Log(idf, 10) * index.GetTermFrequence(word);
        }

        /// <summary>
        /// linguistic score
        /// </summary>
        /// <param name="word"></param>
        /// <returns></returns>
        private static double GetLinguisticScore(WordInfo wordInfo)
        {
            double wordLenScore = (double)wordInfo.Word.Length;
            double wordCateScore = GetWordCateScore(wordInfo.Pos);
            double wordContextScore = 2.0;

            double linguisticScore = wordLenScore * Settings.LinguisticSchemeRate[0] +
                                     wordCateScore * Settings.LinguisticSchemeRate[1] +
                                     wordContextScore * Settings.LinguisticSchemeRate[2];

            return linguisticScore;
        }

        private static double GetWordCateScore(POS pos)
        {
            switch (pos)
            {
                case POS.POS_D_N:
                case POS.POS_A_NR:
                case POS.POS_A_NS:
                case POS.POS_A_NT:
                case POS.POS_A_NZ:
                    return Settings.WordCateRate[0];

                case POS.POS_D_X:
                case POS.POS_A_NX:
                    return Settings.WordCateRate[1];

                case POS.POS_D_C:
                case POS.POS_D_I:
                case POS.POS_D_L:
                case POS.POS_D_E:
                case POS.POS_D_V:
                case POS.POS_D_O:
                case POS.POS_D_P:
                case POS.POS_D_U:
                case POS.POS_D_B:
                case POS.POS_D_D:
                case POS.POS_D_F:
                case POS.POS_D_MQ:
                case POS.POS_D_R:
                case POS.POS_D_Z:
                    return Settings.WordCateRate[3];

                case POS.POS_A_M:
                case POS.POS_A_Q:
                case POS.POS_D_A:
                case POS.POS_D_H:
                case POS.POS_D_K:
                case POS.POS_D_S:
                case POS.POS_D_T:
                case POS.POS_D_W:
                case POS.POS_D_Y:
                case POS.POS_UNK:
                default:
                    return Settings.WordCateRate[2];
            }
        }

        #endregion

        private static bool isPanGuInited = false;
    }
}
